
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>canvas跟随鼠标移动画透明线</title>
    <style>
      div,
      canvas,
      img {
        user-select: none;
      }
      .my_canvas,
      .bg_img {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
      }
      .cf {
        content: "";
        display: block;
        overflow: hidden;
        clear: both;
      }
      .fl {
        float: left;
      }
      .fr {
        float: right;
      }
      .bg_img {
        width: 674px;
        height: 495px;
        background: #ddd;
      }
      .img_tools {
        position: absolute;
        top: 20px;
        left: 50%;
        transform: translateX(-50%);
        border: 1px solid #eee;
        border-radius: 64px;
        height: 64px;
        line-height: 64px;
        box-sizing: border-box;
        padding: 15px 20px 0;
      }
      .img_tool {
        height: 32px;
        line-height: 32px;
        color: #000;
        font-size: 14px;
        text-align: center;
        width: 80px;
        border: 1px solid #ddd;
        border-radius: 32px;
        margin-right: 10px;
        cursor: pointer;
        position: relative;
      }
      .img_tool_active {
        color: #409eff;
        border: 1px solid #409eff;
      }
      .show_history {
        position: absolute;
        bottom: 0;
        left: 50%;
        transform: translateX(-50%);
      }
      .show_history > img {
        width: 120px;
        margin-right: 10px;
        border: 1px solid #eee;
        border-radius: 4px;
      }
      .canvas_text {
        width: 120px;
        height: 32px;
        line-height: 32px;
        position: absolute;
        top: 0;
        left: 0;
        border: 1px solid #c0c0c0;
        border-radius: 4px;
        font-size: 16px;
        outline: none;
        background: none;
        display: none;
        font-family: Arial, Helvetica, sans-serif;
        padding-left: 0;
        letter-spacing: 0;
      }
    </style>
  </head>
  <body>
    <div class="bg_img"></div>
    <canvas
      id="myCanvasBot"
      class="my_canvas"
      width="674"
      height="495"
    ></canvas>
    <canvas
      id="myCanvasTop"
      class="my_canvas"
      width="674"
      height="495"
    ></canvas>
    <div class="img_tools cf">
      <div class="img_tool fl" οnclick="changeType('curve',this)">涂鸦</div>
      <div class="img_tool fl" οnclick="changeType('line',this)">直线</div>
      <div
        class="img_tool fl img_tool_active"
        οnclick="changeType('rect',this)"
      >
        矩形
      </div>
      <div class="img_tool fl" οnclick="changeType('ellipse',this)">圆形</div>
      <!-- <div class="img_tool fl" οnclick="changeType('eraser',this)">橡皮擦</div> -->
      <!-- <div class="img_tool fl" οnclick="changeType('text',this)">文字</div> -->
      <!-- <div class="img_tool fl" οnclick="changeType('revoke',this)">撤销</div> -->
      <!-- <div class="img_tool fl" οnclick="changeType('restore',this)">恢复</div> -->
    </div>
    <input id="canvasText" autofocus class="canvas_text" type="text" />
    <div id="showHistory" class="show_history"></div>
    <script>
      const canvasWidth = 674;
      const canvasHeight = 495;
      //底层canvas
      const botCan = document.getElementById("myCanvasBot");
      //顶层canvas
      const topCan = document.getElementById("myCanvasTop");
      //底层画布
      const botCtx = botCan.getContext("2d");
      //顶层画布
      const topCtx = topCan.getContext("2d");
      //鼠标是否按下  是否移动
      let isDown = false,
        isMove = false;
      //鼠标是否在canvas上抬起
      let isCanUp = false;
      //需要画图的轨迹
      let drawPoints = [];
      //起始点x,y
      let startPoint = {
        x: 0,
        y: 0,
      };
      //图片历史
      let historyList = [];
      //空历史
      historyList.push(new Image());
      //当前绘画历史index
      let historyIndex = -1;
      //icon历史
      // let partHistory = [];
      //操作类型
      let drawType = "rect";
      //画线宽度
      const lineWidth = 10;
      //文字大小
      const fontSize = 16;
      //画线颜色
      let strokeStyle = "rgba(255,0,0,0.6)";
      //path2D图形列表
      let pathList = [];
      //path2D单个图形
      let pathObj = null;
      //path2D的唯一标识
      let pathId = 0;
      //当前被激活的path2D
      let activePath = null;
      //是否为拖拽行为
      let isDrag = false;
      //拖拽是否移动
      let isDragMove = false;
      //是否为改变尺寸行为
      isResize = false;
      //改变尺寸点list
      let pointsList = [];
      //拖拽修改尺寸的点
      let activePoint = null;
      //resize是否移动
      let isResizeMove = false;
      //改变尺寸点
      let resizePath = null;
      //改变尺寸点List
      let resizePointList = [];
      //文字输入框init
      const canvasText = document.getElementById("canvasText");
      canvasText.style.display = "none";
      canvasText.style.lineHeight = "32px";
      canvasText.style.height = "32px";
      canvasText.style.display = "none";
      canvasText.style.color = "none";
      canvasText.addEventListener("blur", () => {
        topCtx.font = fontSize + "px Arial, Helvetica, sans-serif";
        let h = parseFloat(canvasText.style.height);
        topCtx.fillText(
          canvasText.value,
          startPoint.x + 1,
          startPoint.y + h / 2 + fontSize / 2 - 1
        );
        canvasText.style.display = "none";
        canvasText.value = "";
        topToBot();
      });
      //起始点x,y
      let textPoint = {
        x: 0,
        y: 0,
      };
      //鼠标按下
      const mousedown = (e) => {
        isDown = true;
        let x = (e || window.event).offsetX;
        let y = (e || window.event).offsetY;
        if (canvasText.style.display == "none") startPoint = { x, y };
        //检查是否点击到resize的点
        activePoint = isResizePointInPath(x, y);
        if (activePoint) {
          isResize = true;
          activePath = activePoint.activePath; //原有path,需要清除
          //可resize，清除resize点
          topCtx.clearRect(0, 0, canvasWidth, canvasHeight);
          switch (activePoint.type) {
            case "rect":
              makePathActive();
              break;
            case "ellipse":
              makePathActive();
              break;
            case "line":
              makePathActive();
              break;
          }
          return;
        }
        //检测是否点击到图形
        activePath = isPointInPath(x, y);
        if (activePath) {
          createResizePoint(activePath); //只有点击到图形的时候，才添加图形resize的点
          isDrag = true;
          topCtx.strokeStyle =
            topCtx.fillStyle =
            botCtx.strokeStyle =
            botCtx.fillStyle =
              activePath.strokeStyle || strokeStyle;
          topCtx.lineWidth = botCtx.lineWidth =
            activePath.lineWidth || lineWidth;
          topCtx.clearRect(0, 0, canvasWidth, canvasHeight);
          switch (activePath.type) {
            case "rect":
              makePathActive();
              break;
            case "ellipse":
              makePathActive();
              break;
            case "line":
              makePathActive();
              break;
            case "curve":
              makePathActive();
              break;
          }

          return;
        }
        if (drawType == "text") {
          textPoint = {
            x: x + topCan.offsetLeft - canvasWidth / 2,
            y: y + topCan.offsetTop - canvasHeight / 2,
          };
          // canvasText.style.height = 32 + 'px';
          canvasText.style.top = textPoint.y + "px";
          canvasText.style.left = textPoint.x + "px";
          canvasText.style.display = "block";
          canvasText.style.fontSize = fontSize + "px";
          canvasText.style.color = strokeStyle;
          setTimeout(() => {
            canvasText.focus();
          }, 100);
        }
        if (drawType == "curve") {
          drawPoints = [];
          drawPoints.push({ x, y });
        }
        topCtx.strokeStyle =
          topCtx.fillStyle =
          botCtx.strokeStyle =
          botCtx.fillStyle =
            strokeStyle;
        topCtx.lineWidth = botCtx.lineWidth = lineWidth;
        topCtx.lineCap =
          topCtx.lineJoin =
          botCtx.lineCap =
          botCtx.lineJoin =
            "round";
      };
      //鼠标移动
      const mousemove = (e) => {
        let x = (e || window.event).offsetX;
        let y = (e || window.event).offsetY;
        let distanceX = 0;
        let distanceY = 0;
        if (isDown) {
          isMove = true;
          if (isResize) {
            isResizeMove = true;
            if (activePoint && activePoint.resizeFun) {
              activePoint.resizeFun(x, y);
            }
            return;
          }
          if (isDrag) {
            isDragMove = true;
            switch (activePath.type) {
              case "curve":
                distanceX = x - startPoint.x;
                distanceY = y - startPoint.y;
                let newPoints = [];
                for (let i = 0; i < activePath.drawPoints.length; i++) {
                  let drawPoint = activePath.drawPoints[i];
                  newPoints.push({
                    x: drawPoint.x + distanceX,
                    y: drawPoint.y + distanceY,
                  });
                }
                drawCurve(newPoints);
                break;
              case "line":
                distanceX = x - startPoint.x;
                distanceY = y - startPoint.y;
                drawLine(
                  activePath.startX + distanceX,
                  activePath.startY + distanceY,
                  activePath.x + distanceX,
                  activePath.y + distanceY
                );
                break;
              case "eraser":
                // drawEraser(x,y);
                break;
              case "rect":
                // xy 为当前point的坐标
                // startPoint为点击的矩形上点   查看当前point.x点距离startPoint.x移动了多少  point.y点距离startPoint.y移动了多少
                drawRect(
                  activePath.x + (x - startPoint.x),
                  activePath.y + (y - startPoint.y),
                  activePath.width,
                  activePath.height
                );
                break;
              case "ellipse":
                // drawEllipse(x,y);
                drawEllipse(
                  activePath.x + (x - startPoint.x),
                  activePath.y + (y - startPoint.y),
                  activePath.radiusX,
                  activePath.radiusY
                );
                break;
            }
            return;
          }
          switch (drawType) {
            case "curve":
              drawPoints.push({ x, y });
              drawCurve(drawPoints);
              break;
            case "line":
              drawLine(startPoint.x, startPoint.y, x, y);
              break;
            case "eraser":
              drawEraser(x, y);
              break;
            case "rect":
              // drawRect(x,y);
              drawRect(
                startPoint.x,
                startPoint.y,
                x - startPoint.x,
                y - startPoint.y
              );
              break;
            case "ellipse":
              drawEllipse(
                (x + startPoint.x) / 2,
                (y + startPoint.y) / 2,
                Math.abs((x - startPoint.x) / 2),
                Math.abs((y - startPoint.y) / 2),
                0,
                0,
                Math.PI * 2,
                true
              );
              break;
          }
        }
      };
      //鼠标抬起
      const mouseup = (e) => {
        isCanUp = true;
        if (isDown) {
          isDown = false;
          // topCan内容画到botCan上
          if (isResize) {
            isResize = false;
            activePath = null;
            resizePointList = [];
            if (isResizeMove) {
              isResizeMove = false;
              pathList.pop();
            } else {
              pathObj = pathList.pop();
            }
            topToBot();
            return;
          }
          if (isDrag) {
            isDrag = false;
            activePath = null;
            if (isDragMove) {
              isDragMove = false;
              pathList.pop();
            } else {
              pathObj = pathList.pop();
            }
            topToBot();
            return;
          }
          if (drawType != "text") topToBot();
        }
      };
      //topCan内容画到botCan上
      const topToBot = () => {
        if (pathObj) {
          pathObj.id = pathId++;
          pathList.push(pathObj);
          topCtx.clearRect(0, 0, canvasWidth, canvasHeight);
          if (isCanUp) isCanUp = false;
          botCtx[pathObj.shape](pathObj.path);
          //如果有resizePoints
          if (pathObj.points && pathObj.points.length > 0) {
            drawResizePoint(pathObj.points);
          }
          pathObj.points = [];
          pathObj = null;
        }
        drawPoints = [];
        isDown = false;
        isMove = false;
      };
      //判断是否点击到图形
      const isPointInPath = (x, y) => {
        let PointInPath = null;
        for (let i = 0; i < pathList.length; i++) {
          let path = pathList[i];
          if (botCtx.isPointInStroke(path.path, x, y)) {
            PointInPath = path;
            break;
          }
        }
        return PointInPath;
      };
      //判断是否点击到图形
      const isResizePointInPath = (x, y) => {
        let point = null;
        for (let i = 0; i < resizePointList.length; i++) {
          let resizePoint = resizePointList[i];
          if (botCtx.isPointInStroke(resizePoint.path, x, y)) {
            point = resizePoint;
            break;
          }
        }
        return point;
      };
      //激活rect图形轮廓  botCtx删除activePath  topCtx添加activePath
      const makePathActive = () => {
        botCtx.clearRect(0, 0, canvasWidth, canvasHeight);
        let arr = [];
        for (let i = 0; i < pathList.length; i++) {
          let path = pathList[i];
          if (activePath.id != path.id) {
            botCtx[path.shape](path.path);
            arr.push(path);
          } else {
            topCtx.strokeStyle = path.strokeStyle;
            topCtx[path.shape](path.path);
            //创建可拖动的点
          }
        }
        arr.push(activePath);
        pathList = arr;
      };
      //创建resize的点
      const createResizePoint = () => {
        activePath.points = [];
        let ponitFillStyle = "rgba(0,0,0,0.8)";
        let pointWidth = 10;
        let pointHeight = 10;
        switch (activePath.type) {
          case "rect":
            activePath.points = [
              {
                x: activePath.x - pointWidth / 2,
                y: activePath.y - pointHeight / 2,
                width: 10,
                height: 10,
                ponitFillStyle,
                type: activePath.type,
                id: 0,
                activePath: activePath, //用于删除原activePath
                resizeFun: (x, y) => {
                  drawRect(
                    x,
                    y,
                    activePath.x + activePath.width - x,
                    activePath.y + activePath.height - y
                  );
                },
              },
              {
                x: activePath.x + activePath.width / 2 - pointWidth / 2,
                y: activePath.y - pointHeight / 2,
                width: 10,
                height: 10,
                ponitFillStyle,
                type: activePath.type,
                id: 1,
                activePath: activePath,
                resizeFun: (x, y) => {
                  drawRect(
                    activePath.x,
                    y,
                    activePath.width,
                    activePath.height + activePath.y - y
                  );
                },
              },
              {
                x: activePath.x + activePath.width - pointWidth / 2,
                y: activePath.y - pointHeight / 2,
                width: 10,
                height: 10,
                ponitFillStyle,
                type: activePath.type,
                id: 2,
                activePath: activePath,
                resizeFun: (x, y) => {
                  drawRect(
                    activePath.x,
                    y,
                    x - activePath.x,
                    activePath.height + activePath.y - y
                  );
                },
              },
              {
                x: activePath.x + activePath.width - pointWidth / 2,
                y: activePath.y + activePath.height / 2 - pointHeight / 2,
                width: 10,
                height: 10,
                ponitFillStyle,
                type: activePath.type,
                id: 3,
                activePath: activePath,
                resizeFun: (x, y) => {
                  drawRect(
                    activePath.x,
                    activePath.y,
                    x - activePath.x,
                    activePath.height
                  );
                },
              },
              {
                x: activePath.x + activePath.width - pointWidth / 2,
                y: activePath.y + activePath.height - pointHeight / 2,
                width: 10,
                height: 10,
                ponitFillStyle,
                type: activePath.type,
                id: 4,
                activePath: activePath,
                resizeFun: (x, y) => {
                  drawRect(
                    activePath.x,
                    activePath.y,
                    x - activePath.x,
                    y - activePath.y
                  );
                },
              },
              {
                x: activePath.x + activePath.width / 2 - pointWidth / 2,
                y: activePath.y + activePath.height - pointHeight / 2,
                width: 10,
                height: 10,
                ponitFillStyle,
                type: activePath.type,
                id: 5,
                activePath: activePath,
                resizeFun: (x, y) => {
                  drawRect(
                    activePath.x,
                    activePath.y,
                    activePath.width,
                    y - activePath.y
                  );
                },
              },
              {
                x: activePath.x - pointWidth / 2,
                y: activePath.y + activePath.height - pointHeight / 2,
                width: 10,
                height: 10,
                ponitFillStyle,
                type: activePath.type,
                id: 6,
                activePath: activePath,
                resizeFun: (x, y) => {
                  drawRect(
                    x,
                    activePath.y,
                    activePath.x - x + activePath.width,
                    y - activePath.y
                  );
                },
              },
              {
                x: activePath.x - pointWidth / 2,
                y: activePath.y + activePath.height / 2 - pointHeight / 2,
                width: 10,
                height: 10,
                ponitFillStyle,
                type: activePath.type,
                id: 7,
                activePath: activePath,
                resizeFun: (x, y) => {
                  drawRect(
                    x,
                    activePath.y,
                    activePath.x - x + activePath.width,
                    activePath.height
                  );
                },
              },
            ];
            // drawResizePoint( activePath.points);
            break;
          case "ellipse":
            activePath.points = [
              {
                x: activePath.x - pointWidth / 2,
                y: activePath.y - activePath.radiusY - pointHeight / 2,
                width: 10,
                height: 10,
                ponitFillStyle,
                type: activePath.type,
                id: 0,
                activePath: activePath,
                resizeFun: (x, y) => {
                  drawEllipse(
                    activePath.x,
                    activePath.y,
                    activePath.radiusX,
                    Math.abs(y - activePath.y)
                  );
                },
              },
              {
                x: activePath.x + activePath.radiusX - pointWidth / 2,
                y: activePath.y - pointHeight / 2,
                width: 10,
                height: 10,
                ponitFillStyle,
                type: activePath.type,
                id: 0,
                activePath: activePath,
                resizeFun: (x, y) => {
                  drawEllipse(
                    activePath.x,
                    activePath.y,
                    Math.abs(x - activePath.x),
                    activePath.radiusY
                  );
                },
              },
              {
                x: activePath.x - pointWidth / 2,
                y: activePath.y + activePath.radiusY - pointHeight / 2,
                width: 10,
                height: 10,
                ponitFillStyle,
                type: activePath.type,
                id: 0,
                activePath: activePath,
                resizeFun: (x, y) => {
                  drawEllipse(
                    activePath.x,
                    activePath.y,
                    activePath.radiusX,
                    Math.abs(y - activePath.y)
                  );
                },
              },
              {
                x: activePath.x - activePath.radiusX - pointWidth / 2,
                y: activePath.y - pointHeight / 2,
                width: 10,
                height: 10,
                ponitFillStyle,
                type: activePath.type,
                id: 0,
                activePath: activePath,
                resizeFun: (x, y) => {
                  drawEllipse(
                    activePath.x,
                    activePath.y,
                    Math.abs(x - activePath.x),
                    activePath.radiusY
                  );
                },
              },
            ];
            // drawResizePoint( activePath.points);
            break;
          case "line":
            activePath.points = [
              {
                x: activePath.startX - pointWidth / 2,
                y: activePath.startY - pointHeight / 2,
                width: 10,
                height: 10,
                ponitFillStyle,
                type: activePath.type,
                id: 0,
                activePath: activePath,
                resizeFun: (x, y) => {
                  drawLine(x, y, activePath.x, activePath.y);
                },
              },
              {
                x: activePath.x - pointWidth / 2,
                y: activePath.y - pointHeight / 2,
                width: 10,
                height: 10,
                ponitFillStyle,
                type: activePath.type,
                id: 0,
                activePath: activePath,
                resizeFun: (x, y) => {
                  drawLine(activePath.startX, activePath.startY, x, y);
                },
              },
            ];
            // drawResizePoint( activePath.points);
            break;
        }
        drawResizePoint(activePath.points);
      };
      //画resize的点
      const drawResizePoint = (points) => {
        resizePointList = [];
        for (let i = 0; i < points.length; i++) {
          let resizePoint = points[i];
          let path = new Path2D();
          topCtx.beginPath();
          topCtx.moveTo(resizePoint.x, resizePoint.y);
          path.rect(
            resizePoint.x,
            resizePoint.y,
            resizePoint.width,
            resizePoint.height
          );
          topCtx.strokeStyle = resizePoint.ponitFillStyle;
          topCtx.stroke(path);
          resizePoint.path = path;
          resizePointList.push(resizePoint);
        }
      };
      //画椭圆形
      const drawEllipse = (x, y, radiusX, radiusY) => {
        pathObj = null;
        //清除topCtx画布
        topCtx.clearRect(0, 0, canvasWidth, canvasHeight);
        topCtx.beginPath();
        let path = new Path2D();
        // 椭圆
        path.ellipse(x, y, radiusX, radiusY, 0, 0, Math.PI * 2, true);
        topCtx.strokeStyle = strokeStyle;
        topCtx.stroke(path);
        pathObj = {
          type: "ellipse",
          shape: "stroke",
          path,
          x,
          y,
          radiusX,
          radiusY,
          lineWidth: topCtx.lineWidth || lineWidth,
          strokeStyle: topCtx.strokeStyle || strokeStyle,
          points: [],
        };
      };
      //画矩形
      const drawRect = (x, y, width, height) => {
        pathObj = null;
        //清除topCtx画布
        topCtx.clearRect(0, 0, canvasWidth, canvasHeight);
        topCtx.beginPath();
        let path = new Path2D();
        // 矩形
        //这里需要确认起始点和结束点
        //即  不管画出的矩形是向上，向下 左上角点为起始点，做下角点为结束点
        let beginX = width < 0 ? x + width : x;
        let beginY = height < 0 ? y + height : y;

        path.rect(beginX, beginY, Math.abs(width), Math.abs(height));
        topCtx.strokeStyle = strokeStyle;
        topCtx.stroke(path);
        pathObj = {
          type: "rect",
          shape: "stroke",
          path,
          x: beginX,
          y: beginY,
          width: Math.abs(width),
          height: Math.abs(height),
          lineWidth: topCtx.lineWidth || lineWidth,
          strokeStyle: topCtx.strokeStyle || strokeStyle,
          points: [],
        };
      };
      //橡皮擦
      const drawEraser = (x, y) => {
        //橡皮擦圆形半径
        const radius = lineWidth / 2;
        botCtx.beginPath();
        for (let i = 0; i < radius * 2; i++) {
          //勾股定理高h
          let h = Math.abs(radius - i); //i>radius h = i-radius; i<radius  h = radius - i
          //勾股定理l
          let l = Math.sqrt(radius * radius - h * h);
          //矩形高度
          let rectHeight = 1;
          //矩形宽度
          let rectWidth = 2 * l;
          //矩形X
          let rectX = x - l;
          //矩形Y
          let rectY = y - radius + i;

          botCtx.clearRect(rectX, rectY, rectWidth, rectHeight);
        }
      };
      //画透明度直线
      const drawLine = (startX, startY, x, y) => {
        if (!isDown) return;
        pathObj = null;
        //清空当前画布内容
        topCtx.clearRect(0, 0, canvasWidth, canvasHeight);
        //必须每次都beginPath  不然会卡
        topCtx.beginPath();
        let path = new Path2D();
        path.moveTo(startX, startY);
        path.lineTo(x, y);
        topCtx.strokeStyle = strokeStyle;
        topCtx.stroke(path);
        pathObj = {
          type: "line",
          shape: "stroke",
          path,
          x,
          y,
          startX,
          startY,
          lineWidth: topCtx.lineWidth || lineWidth,
          strokeStyle: topCtx.strokeStyle || strokeStyle,
          points: [],
        };
      };
      //画带透明度涂鸦
      const drawCurve = (drawPointsParams) => {
        // drawPoints.push({x,y});
        if (!drawPointsParams || drawPointsParams.length < 1) return;
        pathObj = null;
        //清空当前画布内容
        topCtx.clearRect(0, 0, canvasWidth, canvasHeight);
        //必须每次都beginPath  不然会卡
        topCtx.beginPath();
        let path = new Path2D();
        path.moveTo(drawPointsParams[0].x, drawPointsParams[0].y);
        for (let i = 1; i < drawPointsParams.length; i++) {
          path.lineTo(drawPointsParams[i].x, drawPointsParams[i].y);
        }
        topCtx.strokeStyle = strokeStyle;
        topCtx.stroke(path);
        pathObj = {
          type: "curve",
          shape: "stroke",
          path,
          drawPoints: drawPointsParams,
          lineWidth: topCtx.lineWidth || lineWidth,
          strokeStyle: topCtx.strokeStyle || strokeStyle,
        };
      };
      //切换操作
      const changeType = (type, that) => {
        // if(drawType == type) return;
        let tools = document.getElementsByClassName("img_tool");
        for (let i = 0; i < tools.length; i++) {
          let ele = tools[i];
          if (ele.classList.contains("img_tool_active"))
            ele.classList.remove("img_tool_active");
        }
        that.classList.add("img_tool_active");
        drawType = type;
        //撤销
        if (drawType == "revoke") {
          if (historyIndex > 0) {
            historyIndex--;
            drawImage(historyList[historyIndex]);
          }
          //恢复
        } else if (drawType == "restore") {
          if (historyIndex < historyList.length - 1) {
            historyIndex++;
            drawImage(historyList[historyIndex]);
          }
        }
      };
      const drawImage = (img) => {
        botCtx.clearRect(0, 0, canvasWidth, canvasHeight);
        botCtx.drawImage(img, 0, 0);
      };

      //canvas添加鼠标事件
      topCan.addEventListener("mousedown", mousedown);
      topCan.addEventListener("mousemove", mousemove);
      topCan.addEventListener("mouseup", mouseup);
      //全局添加鼠标抬起事件
      document.addEventListener("mouseup", (e) => {
        let x = (e || window.event).offsetX;
        let y = (e || window.event).offsetY;
        let classList = (e.target || {}).classList || [];
        if (classList.contains("img_tool")) return;
        if (!isCanUp) {
          isDown = false;
          // topCan内容画到botCan上
          if (isDrag) {
            isDrag = false;
            activePath = null;
            if (isDragMove) {
              isDragMove = false;
              pathList.pop();
            } else {
              pathObj = pathList.pop();
            }
            topToBot();
            return;
          }
          if (drawType == "line" && !isDrag) {
            let clientX = topCan.getBoundingClientRect().x;
            let clientY = topCan.getBoundingClientRect().y;
            drawLine(startPoint.x, startPoint.y, x - clientX, y - clientY);
          }
          // topCan内容画到botCan上
          topToBot();
        }
      });
      //全局添加鼠标移动事件
      document.addEventListener("mousemove", (e) => {
        if (isMove) return (isMove = false);
        let x = (e || window.event).offsetX;
        let y = (e || window.event).offsetY;
        if (drawType == "line" && !isDrag) {
          let clientX = topCan.getBoundingClientRect().x;
          let clientY = topCan.getBoundingClientRect().y;
          drawLine(startPoint.x, startPoint.y, x - clientX, y - clientY);
        }
      });
    </script>
  </body>
</html>

